Repository navigation
Fix yarn renamed-copy identity fail-open (#1516) - #1517
Merged
Mikola Lysenko (mikolalysenko) merged 7 commits intoOct 14, 2026
Merged
Conversation
Assisted-by: Claude Code:claude-opus-5-5
A renamed yarn copy from a file: path outside the project, a tarball whose top directory isn't package/, or a non-registry URL was taken for "not this package". The scan then succeeded and VEX attested not_affected while that copy installed unpatched (#1516). Read outside-root copies where yarn installs them from, strip a tarball's first path segment as yarn does, and fail closed: an unreadable copy of the same version is named as a possible copy and blocks attestation of that version from the lock. WIP: tests written, not yet run. Assisted-by: Claude Code:claude-opus-5-5
Describe in docs/ecosystems.md how renamed yarn copies outside the project, tarballs with any top-level directory, and copies whose package can't be read are treated (#1516). Assisted-by: Claude Code:claude-opus-5-5
Assisted-by: Claude Code:claude-opus-5-5
Match a copy whose package can't be read against the lock's refs once every extractor is done, so berry vendored and resolutions refs pushed after the copies are read are contested too, in one pass. Take a classic file: path that leaves the root as root-relative only when the root package.json declares it: yarn 1 writes a workspace member's "file:../b" relative to that member, so reading it from the root would name the wrong package or block attestation for nothing. Never read an absolute copy path from a lock, which could name any file or a Windows network share; such a copy stays unidentified. Assisted-by: Claude Code:claude-opus-5-5
The CLI builds Discovery with struct update syntax, so a private field broke its build. Assisted-by: Claude Code:claude-opus-5-5
Mikola Lysenko (mikolalysenko)
marked this pull request as ready for review
October 14, 2026 03:21
Collaborator
Author
|
BugBot review Generated by Claude Code |
There was a problem hiding this comment.
✅ Bugbot reviewed your changes and found no new issues!
Comment @cursor review or bugbot run to trigger another review on this PR
Reviewed by Cursor Bugbot for commit 6289505. Configure here.
yarn 1 writes a file: lock key relative to the project root and normalized, whichever package.json declared it: a workspace member's "file:../../../ext/lpdir" locks as "lp2@file:../ext/lpdir", and the root's "file:../ext/lpdir/" loses its trailing slash. The gate that took an outside-root key as the root's own only when the root package.json spelled it exactly dropped both, so the unpatched copy went unwarned and vex attested it again. Drop the gate. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01WyDjcHE4ZNtbUocsBEJvFH
Tanmay Singla (Tanmay182003)
approved these changes
Oct 14, 2026
Mikola Lysenko (mikolalysenko)
deleted the
agent/fix-yarn-copy-identity-fail-open
branch
October 14, 2026 04:56
This was referenced Oct 14, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
LLM Description written by Claude Code:claude-opus-5-5
Fixes #1516
Summary
A renamed yarn copy (
"lp2": "file:../ext/lpdir", locked aslp2@file:../ext/lpdir) is now identified, or warned about, in three shapes that used to slip through:file:copy outside the project rootfile:tarball whose top directory isn'tpackage/Before this fix, a hosted or vendored scan exited 0 with
successand VEX attestednot_affectedfor left-pad@1.3.0, whilenode_modules/lp2installed it unpatched. Now:redirect_copy_unpatchedvexand the in-run--vexdon't attest the package from that lockRoot cause
#1489 identifies a renamed copy by reading the copy itself (
formats::yarn::copy::CopyRef). Hosted, vendored andvexall share that reader, and every caller treated a copy it couldn't read as "not this package" (fail-open):classic_copy_refdropped anyfile:path that leaves the root (normalize_rel("", path)?).tarball_manifest_nameused the lenient archive reader, which strips only a literalpackage/prefix. yarn strips the first path segment whatever its name.Fix
copy.rs: a copy's path keeps its../escapes and is read where yarn installs it from.file:key relative to the root and normalized, whicheverpackage.jsondeclared it (checked with 1.22.22: a member'sfile:../../../ext/lpdirlocks aslp2@file:../ext/lpdir), so each one is read from the root.locator=workspace.patch/package.rs: newread_archive_bytes_to_map_any_root, which strips the first segment the way npm-family installers do. A member spelled under two different roots is dropped as ambiguous.portal:/link:):vexdiscovery records it withDiscovery::unidentified_copy.contest_within_locksmatches it once every extractor has pushed its refs, as an unpatched copy of each package that lock wires at the copy's version. Hosted mode reads the same result, so it warnsredirect_copy_unpatchedand keeps the in-run VEX from attesting.vendor::yarn_copies::other_name_copiesreturnsOtherNameCopy { identified }. An unidentified copy is named as one that "may be" the package, but it never turnsvendor_lock_entry_not_foundintonot_rewritable: it doesn't prove the package is installed.docs/ecosystems.mddocuments the behavior.Tests (red on main, green here)
file:dir/tarball,left-pad-1.3.0/tarball root, non-registry URL, absolute path (vex/hosted discovery, hosted and vendored wirings)vex::discover::yarn::tests::issue_1516_unread_classic_copies_contest_the_refvex::discover::yarn::tests::issue_1516_unread_berry_copy_contests_a_vendored_refvendor::yarn_classic_lock::tests::issue_1516_unread_other_name_copies_are_namednot_foundvendor::yarn_classic_lock::tests::issue_1516_unread_copy_alone_is_not_foundformats::yarn::copy::tests::{tarball_identity_reads_any_root_directory, classic_refs_split_tarballs_directories_and_urls}vex)e2e_redirect_yarn_classic_build::classic_outside_root_copy_under_another_name_is_named_and_not_attestedstatus: success, 1 VEX statement)Commands run locally:
cargo fmt --all -- --check: okcargo clippy --locked --workspace --all-features -- -D warnings(CI's command): okcargo test --workspace --all-features --lib --bins: all pass except 4 tests outside this diff (copy_tree::relax_loop_must_not_traverse_symlinked_root,vlt_heal::an_unremovable_hidden_lock_keeps_every_store_entry,pypi_poetry::wire_write_failure_…,pypi_requirements::wire_failure_…). Each relies on a read-only directory blocking a write, and this sandbox runs as uid 0. They don't touch yarn code.SOCKET_PATCH_YARN_CLASSIC_E2E_VERSION=1.22.22:e2e_redirect_yarn_classic_build40/40,e2e_vendor_yarn_classic_build40/40,e2e_vendor_yarn_classic_dev_flow36/36.cargo test --workspace(every integration binary) doesn't fit this session's disk allowance (link fails with ENOSPC). CI runs it.npm/,pypi/,gem/only dispatch to the binary, so nothing to change there./code-review highfindingsFixed:
contest_within_locks. It also runs once per run (version index) instead of copies × refs.file:../bwas suspected of being read relative to the wrong directory. A reviewer then showed that yarn 1 writes these keys root-relative, and the gate I first added for it dropped real copies, so it has been removed (75d5610).Not changed, with reasons:
not_affected.classic_file_directoryskip doesn't fire for outside-root paths: the engine's discovery pass names the copy withredirect_copy_unpatched(asserted by the e2e). Unifying the rewriter's resolver withCopyRefis a refactor beyond this fix.../proj/.socket/…spelling of our own wiring: yarn writes.socketwiring root-relative (file:./.socket/…). A lock that spells it through../<own-dir>isn't something socket-patch or yarn produces.🤖 Generated with Claude Code
https://claude.ai/code/session_01WyDjcHE4ZNtbUocsBEJvFH
Generated by Claude Code